﻿using System;
using Yuki.Sockets;

namespace Yuki.Sockets.Encryptions
{
    public unsafe class ConquerStanderedCipher : IPacketCipher
    {
        #region Private Variables
        private byte[] Key3;
        private byte[] Key4;
        private ushort InCounter = 0;
        private ushort OutCounter = 0;
        private bool UsingAlternate = false;
        #endregion
        public void Encrypt(byte* In, byte[] Out, int Length)
        {
            lock (this)
            {
                for (int i = 0; i < Length; i++)
                {
                    Out[i] = (byte)(In[i] ^ 0xAB);
                    Out[i] = (byte)((Out[i] << 4) | (Out[i] >> 4));
                    Out[i] = (byte)(ConquerKeys.Key2[OutCounter >> 8] ^ Out[i]);
                    Out[i] = (byte)(ConquerKeys.Key1[OutCounter & 0xFF] ^ Out[i]);
                    OutCounter = (ushort)(OutCounter + 1);
                }
            }
        }
        public void Encrypt(byte[] In, byte[] Out, int Length)
        {
            lock (this)
            {
                for (int i = 0; i < Length; i++)
                {
                    Out[i] = (byte)(In[i] ^ 0xAB);
                    Out[i] = (byte)((Out[i] << 4) | (Out[i] >> 4));
                    Out[i] = (byte)(ConquerKeys.Key2[OutCounter >> 8] ^ Out[i]);
                    Out[i] = (byte)(ConquerKeys.Key1[OutCounter & 0xFF] ^ Out[i]);
                    OutCounter = (ushort)(OutCounter + 1);
                }
            }
        }
        public void Decrypt(byte[] In, byte[] Out, int Length)
        {
            lock (this)
            {
                for (int i = 0; i < Length; i++)
                {
                    Out[i] = (byte)(In[i] ^ 0xAB);
                    Out[i] = (byte)((Out[i] << 4) | (Out[i] >> 4));
                    if (UsingAlternate)
                    {
                        Out[i] = (byte)(Key4[InCounter >> 8] ^ Out[i]);
                        Out[i] = (byte)(Key3[InCounter & 0xFF] ^ Out[i]);
                    }
                    else
                    {
                        Out[i] = (byte)(ConquerKeys.Key2[InCounter >> 8] ^ Out[i]);
                        Out[i] = (byte)(ConquerKeys.Key1[InCounter & 0xFF] ^ Out[i]);
                    }
                    InCounter = (ushort)(InCounter + 1);
                }
            }
        }
        public unsafe void SetKeys(uint inKey1, uint inKey2)
        {
            uint dwKey1 = ((inKey1 + inKey2) ^ 0x4321) ^ inKey1;
            uint dwKey2 = (uint)(dwKey1 * dwKey1);
            Key3 = new byte[256];
            Key4 = new byte[256];
            fixed (void* uKey1 = ConquerKeys.Key1, uKey3 = Key3,
                         uKey2 = ConquerKeys.Key2, uKey4 = Key4)
            {
                const byte dwKeyLoop = 256 / 4;
                for (byte i = 0; i < dwKeyLoop; i++)
                {
                    *(((uint*)uKey3) + i) = dwKey1 ^ *(((uint*)uKey1) + i);
                    *(((uint*)uKey4) + i) = dwKey2 ^ *(((uint*)uKey2) + i);
                }
            }
            OutCounter = 0;
            UsingAlternate = true;
        }
    }
}

